package com.agile.leetcode.string;

import org.junit.Test;

/**
 * @Author:ChenZhangKun
 * @Date: 2021/6/17 9:45
 */
public class StringTest {
    /**
     * "test"、"te"、"st"；在堆上会创建多少个对象？答案是四个：a指向的"test"对象，两个匿名对象"te"、"st"，b指向的"test"对象
     */
    @Test
    public void test_1() {
        String a = new String("test");
        String b = new String("te") + new String("st");
        System.out.println(a.equals(b)); // true
        System.out.println(a == b); // false
        System.out.println(System.identityHashCode(a));
        System.out.println(System.identityHashCode(b));
    }

    @Test
    public void test_2() {
        // 指向常量池的对象
        String a = "test";
        // 指向常量池中的对象
        // 这是因为形如"te"+"st"这种由多个字符串常量连接而成的字符串在编译期被认为是一个字符串常量
        String b = "te" + "st";
        System.out.println(a.equals(b)); // true
        System.out.println(a == b); // true
        System.out.println(System.identityHashCode(a));
        System.out.println(System.identityHashCode(b));
    }

    /**
     * 对象d的初始化方式似乎跳出了之前所说的两种初始化方式，是由两个存在于字符串常量池中的字符串对象连接生成，
     * 但其实此时的d的初始化方式只是复杂了一些，并没有脱离之前所说的两种初始化方式，具体步骤如下：
     * b、c已经存放在字符串常量池中，将b、c对象复制到堆中；
     * <p>
     * 在堆中新建d对象，并将b、c连接后的值赋给d；
     */
    @Test
    public void test_3() {
        String a = "test";
        String b = "te";
        String c = "st";
        String d = b + c;
        System.out.println(a.equals(d)); // true
        System.out.println(a == d); // false
        System.out.println(System.identityHashCode(a)); // 1639705018
        System.out.println(System.identityHashCode(d)); // 1627674070
    }

    /**
     * 与案例六不同的是b、c都加了final修饰，在这种情况下，与案例四一样，编译器将b+c视为了字符串常量，
     * 所以d指向的是在字符串常量池中已存在的"test"对象
     */
    @Test
    public void test_4() {
        String a = "test";
        final String b = "te";
        final String c = "st";
        String d = b + c;
        System.out.println(a.equals(d)); // true
        System.out.println(a == d); // true
        System.out.println(System.identityHashCode(a)); // 1639705018
        System.out.println(System.identityHashCode(d)); // 1639705018
    }

    /**
     * 当a初始化时，"test"对象被加入字符串常量池，然后在堆中创建"test"对象，a指向堆中"test"对象；
     * <p>
     * b初始化同a，但是不像字符串常量池中那样，虽然a与b的内容相同，但是依然会在堆上创建两个对象；
     * <p>
     * 所以a.equals(b)为true，a==b为false
     */
    @Test
    public void test_5() {
        // test会进入常量池,但是a指向堆中的test地址
        String a = new String("test");
        //
        String b = new String("test");
        System.out.println(a.equals(b)); // true
        System.out.println(a == b); // false
        System.out.println(System.identityHashCode(a)); // 1639705018
        System.out.println(System.identityHashCode(b)); // 1627674070
    }

    /**
     * 当调用intern方法时，如果字符串常量池中已经包含一个内容相同的String对象，则返回池中的对象。
     * 否则，将在字符串常量池中添加一个该对象在堆中引用地址，
     * 并返回该对象地址值（在jdk6中，是直接将此String对象添加到池中，并返回此String对象）。
     *
     *
     * 此时a初始化完成后指向的对象的位置是在堆上的，字符串常量池中没有相同内容的String对象（这里指的是test），执行a = a.intern()后
     *
     * 字符串常量池中增加一个引用，该引用指向堆上的String对象，所以a依然指向的是堆上的String对象，执行前后，hashcode没有变化；
     * （如果在jdk6 执行此代码，获得结果是不一样的-分别是用字符串常量池的地址与堆得地址做比较）
     */
    @Test
    public void test_6() {
        String a = new String("te") + new String("st");
        System.out.println(System.identityHashCode(a)); // 1639705018
        a = a.intern();
        System.out.println(System.identityHashCode(a)); // 1639705018
    }

    /**
     * 此时a初始化完成后指向的对象的位置是在堆上的，同时在字符串常量池中有一个相同内容的String对象，执行a = a.intern()后，
     * 指向位置是在字符串常量池中
     */
    @Test
    public void test_7() {
        String a = new String("test");
        System.out.println(System.identityHashCode(a)); // 1639705018
        a = a.intern();
        System.out.println(System.identityHashCode(a)); // 1627674070
    }
}
